home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Snippets / Stuart's Tech Notes / Blobby password entry.c < prev    next >
Text File  |  1996-04-28  |  6KB  |  191 lines

  1. // (C) 1992-1995 Stuart Cheshire <cheshire@cs.stanford.edu>
  2. //
  3. // This is password entry dialog box code, with a special custom FilterProc to
  4. // do the 'blobby' password field because Apple provides no standard dialog item
  5. // to give that behaviour. More re-inventing the wheel. It also checks whether
  6. // any of the password is typed with Caps Lock pressed -- so that the user can
  7. // be given a hint about the probable reason if their password is rejected.
  8.  
  9. #define USERNAME_RESOURCE_ID 128
  10. #define CAPS_LOCK 0x39
  11.  
  12. // Dialogs in program
  13. enum { dialog_password = 128 };
  14.  
  15. // Items in the password dialog
  16. enum { dlog_OK = 1, dlog_Cancel, dlog_useritem, dlog_username, dlog_password };
  17.  
  18. typedef struct
  19.     {
  20.     Str255  user, pass;
  21.     Boolean EnableOK;
  22.     Boolean OutlineOK;
  23.     Boolean OldOutlineOK;
  24.     Boolean caps_pressed;
  25.     } PromptInfo;
  26.  
  27. local pascal void doOutlineOK(DialogPtr dlog, short item)
  28.     {
  29.     PromptInfo *info = (PromptInfo *)GetWRefCon(dlog);
  30.     int i;
  31.     short    di_type;
  32.     Handle    di_handle;
  33.     Rect    di_box;
  34.     GetDItem(dlog, dlog_OK, &di_type, &di_handle, &di_box);
  35.     InsetRect(&di_box,-4,-4);
  36.     PenNormal();
  37.     PenSize(3,3);
  38.     if (!info->OutlineOK) ForeColor(whiteColor);
  39.     FrameRoundRect(&di_box,16,16);
  40.     PenNormal();
  41.     ForeColor(blackColor);
  42.     info->OldOutlineOK = info->OutlineOK;
  43.     }
  44.  
  45. static Boolean ClickButton(DialogPtr dlog, EventRecord *event, short *itemHit, short item)
  46.     {
  47.     short            di_type;
  48.     ControlHandle    di_handle;
  49.     Rect            di_box;
  50.     GetDItem(dlog, item, &di_type, (Handle*)&di_handle, &di_box);
  51.  
  52.     // If button not disabled, click it
  53.     if (di_handle[0]->contrlHilite != 255)
  54.         {
  55.         long finalTick;
  56.         HiliteControl(di_handle, inButton);
  57.         Delay(5, &finalTick);
  58.         HiliteControl(di_handle, 0);
  59.         *itemHit = item;
  60.         return(true);
  61.         }
  62.     else    // else, ignore the event
  63.         {
  64.         event->what = nullEvent;
  65.         return(false);
  66.         }
  67.     }
  68.  
  69. static void DeleteRange(unsigned char *buffer, short start, short end)
  70.     {
  71.     unsigned char *last = buffer + buffer[0];
  72.     unsigned char *src  = buffer + 1 + end;
  73.     unsigned char *dest = buffer + 1 + start;
  74.     while (src <= last) *dest++ = *src++;        // Close up gap in string
  75.     buffer[0] -= (end-start);                    // Adjust the buffer's length
  76.     }
  77.  
  78. static void InsertChar(unsigned char *buffer, short pos, char c)
  79.     {
  80.     register short i = buffer[0];
  81.     if (i == 0xFF) return;    // return if string already full
  82.     while (i > pos) { buffer[i+1] = buffer[i]; i--; }
  83.     buffer[pos+1] = c;        // Fill in the new character
  84.     buffer[0]++;            // Add one to the length of the string
  85.     }
  86.  
  87. static pascal Boolean FilterProc(DialogPtr dlog, EventRecord *event, short *itemHit)
  88.     {
  89.     PromptInfo *info = (PromptInfo *)GetWRefCon(dlog);
  90.     char key = event->message & charCodeMask;
  91.     Boolean editing_password = (((DialogPeek)dlog)->editField == dlog_password-1);
  92.     
  93.     info->OutlineOK = info->EnableOK && (editing_password || info->pass[0]);
  94.     if (info->OldOutlineOK != info->OutlineOK) doOutlineOK(dlog, 1);
  95.  
  96.     // Only do special processing for keyboard events
  97.     if (event->what != keyDown && event->what != autoKey) return(false);
  98.     
  99.     // if in password field, or if password field is already filled in, Return & Enter
  100.     // are equivalent to hitting the OK button
  101.     // Otherwise they are equivalent to a TAB, to move us into the password field.
  102.     // This is because Unix users often press return after entering their user name
  103.     // instead of TAB -- no need to make there lives any harder.
  104.     
  105.     if (key==3 || key==13)
  106.         {
  107.         // If OK button is outlined, then pressing <Return> hits it
  108.         if (info->OutlineOK) return(ClickButton(dlog, event, itemHit, 1));
  109.         else { event->message = '\t'; return(false); }
  110.         }
  111.     
  112.     // Escape or Command-Period hits cancel
  113.     if (key==27 || (key == '.' && event->modifiers & cmdKey))
  114.         return(ClickButton(dlog, event, itemHit, 2));
  115.     
  116.     // Ignore command keys
  117.     if (event->modifiers & cmdKey) { event->what = nullEvent; return(false); }
  118.     
  119.     // All keys except Tab and cursor keys get our special treatment
  120.     if (editing_password && key!='\t' && (key<28 || key>31))
  121.         {
  122.         short start = (*((DialogPeek)dlog)->textH)->selStart;    // Get current selection
  123.         short end   = (*((DialogPeek)dlog)->textH)->selEnd;
  124.         if (start > info->pass[0]) start = info->pass[0];        // Sanity checks,
  125.         if (end   > info->pass[0]) end   = info->pass[0];        // just in case
  126.         if (start != end) DeleteRange(info->pass,start,end);    // If there's a selection, delete it
  127.             
  128.         // If not delete key then stash the key and change code to a blob, else
  129.         // see if we have to delete a single character (when there is no selection)
  130.         if (key != 8)
  131.             {
  132.             unsigned char km[16];
  133.             GetKeys((unsigned long *)km);
  134.             if (km[CAPS_LOCK>>3] & 1 << (CAPS_LOCK & 7)) info->caps_pressed = TRUE;
  135.             InsertChar(info->pass,start,key);
  136.             event->message = '•';
  137.             }
  138.         else if (start == end && start > 0) DeleteRange(info->pass,start-1,start);
  139.         }
  140.     return(false);         // Let ModalDialog insert the fake char
  141.     }
  142.  
  143. static void do_dialog(void)
  144.     {
  145.     Handle  username = GetResource('STR ', USERNAME_RESOURCE_ID);
  146.     PromptInfo info;
  147.     short    di_type, item;
  148.     Handle    di_handle;
  149.     Rect    di_box;
  150.     DialogPtr modal = GetNewDialog(dialog_password, NULL, (WindowPtr)(-1));
  151.     
  152.     GetDItem(modal, dlog_useritem, &di_type, &di_handle, &di_box);
  153.     SetDItem(modal, dlog_useritem,  di_type, (Handle)doOutlineOK, &di_box);
  154.  
  155.     GetDItem(modal, dlog_username, &di_type, &di_handle, &di_box);
  156.     SetIText(di_handle, (StringPtr)*username);    // Get username from resource
  157.     GetIText(di_handle, info.user);                // and initialize 'user' with it.
  158.     SelIText(modal, dlog_username, 0, 32767);
  159.     
  160.     info.pass[0] = 0;
  161.     info.EnableOK = (info.user[0] > 0);
  162.     info.OutlineOK = info.OldOutlineOK = info.caps_pressed = FALSE;
  163.     SetWRefCon(modal,(long)&info);
  164.     ShowWindow(modal);
  165.  
  166.     while(TRUE)
  167.         {
  168.         SetPort(modal);
  169.         GetDItem(modal, dlog_OK, &di_type, &di_handle, &di_box);
  170.         HiliteControl((ControlHandle)di_handle, info.EnableOK ? 0 : 255);
  171.         
  172.         ModalDialog(FilterProc, &item);
  173.         GetDItem(modal, dlog_username, &di_type, &di_handle, &di_box);
  174.         GetIText(di_handle, info.user);
  175.         info.EnableOK = (info.user[0] > 0);
  176.         
  177.         if (item == dlog_Cancel) break;
  178.         else if (item == dlog_OK)
  179.             {
  180.             // do required stuff with (info.user, info.pass);
  181.             
  182.             // if password is bad, and info.caps_pressed is TRUE,
  183.             // maybe warn the user and try again?
  184.             
  185.             break;
  186.             }
  187.         }
  188.     ReleaseResource(username);
  189.     DisposDialog(modal);
  190.     }
  191.